///<reference path="../../../../libs/proto/protolist.d.ts"/>
/**
 * Created by lintao_alex on 2017/9/18.
 */


namespace game {
    import ByteArray = egret.ByteArray;
    import CommonResult = protolist.CommonResult;
    import PlayerReconnectParam = protolist.PlayerReconnectParam;
    import ObjectPool = mengdao.ObjectPool;
    import BaseNetCommandMapper = mengdao.BaseNetCommandMapper;
    import warn = egret.warn;
    import ServerGetter = mengdao.ServerGetter;
    import BaseService = mengdao.BaseService;

    export class NetService extends BaseService{
        private readonly LOG_MARK: string = '[socket] '
        private _socket: egret.WebSocket;
        private _onConnectCall: Function;
        private _onConnectObj: any;

        private _pushMap: Map<string, BackCall[]>;
        private _packageIdList: number[];
        private _packageList: NetCallNode[];
        private _timer: TimerVO;

        private _commandMapper: BaseNetCommandMapper;
        private _server: ServerGetter;

        public sendRequest(command: string, request?: any, thisObj?: any, responseCall?: Function, errorCall?: Function) {
            let node: NetCallNode = ObjectPool.getObj(NetCallNode);
            node.command = command;
            node.reset(request, thisObj, responseCall, errorCall);
            this._packageIdList.push(node.id);
            this._packageList.push(node);
        }

        public registerPush(command: string, callFuc: Function, callObj: any) {
            let callList = this._pushMap.get(command);
            if (!callList) {
                callList = [];
                this._pushMap.set(command, callList);
            } else {
                for (var i = callList.length - 1; i >= 0; i--) {
                    var node = callList[i];
                    if (node.sameWith(callFuc, callObj)) {
                        return;
                    }
                }
            }
            let newNode: BackCall = ObjectPool.getObj(BackCall);
            newNode.reset(callFuc, callObj);
            callList.push(newNode);
        }

        public unRegisterPush(command: string, callFuc: Function, callObj: any) {
            let callList = this._pushMap.get(command);
            if (callList) {
                for (var i = callList.length - 1; i >= 0; i--) {
                    var node = callList[i];
                    if (node.sameWith(callFuc, callObj)) {
                        callList.slice(i, 1);
                        if (callList.length == 0) {
                            this._pushMap.delete(command);
                        }
                        break;
                    }
                }
            }
        }

        public init(onConnectCall: Function, onConnectObj: any, server: ServerGetter) {
            this._server = server;
            this._commandMapper = new CommandProtoMapper();
            this._onConnectCall = onConnectCall;
            this._onConnectObj = onConnectObj;
            this._packageIdList = [];
            this._packageList = [];
            this._pushMap = new Map<string, BackCall[]>();

            let mSocket = new egret.WebSocket();
            this._socket = mSocket;
            this._socket.type = egret.WebSocket.TYPE_BINARY;
            this._socket.addEventListener(egret.ProgressEvent.SOCKET_DATA, this.onReceiveData, this);
            this._socket.addEventListener(egret.IOErrorEvent.IO_ERROR, this.onIoError, this);
            this._socket.addEventListener(egret.Event.CLOSE, this.onSocketClose, this);
            this._socket.addEventListener(egret.Event.CONNECT, this.onSocketConnect, this);
            this.connectToServer();
            this.socketLog('ready connect to [' + this._server.protocol + '://' + this._server.host + ':' + this._server.port + ']');
            this._timer = TimerProxy.instance.setInterval(this.sendInterval, 100, 0, this);
        }

        private connectToServer() {
            if (this._server.isWSS) {
                let url = "wss://" + this._server.host + ":" + this._server.port;
                this._socket.connectByUrl(url);
            }
            else {
                this._socket.connect(this._server.host, this._server.port);
            }
        }

        private sendInterval() {
            for (let i = 0; i < this._packageIdList.length; i++) {
                if (!this._packageList[i].sended) {
                    this.sendPackage(this._packageList[i])
                    this._packageList[i].sended = true;
                    break;
                }
            }
        }

        private sendPackage(node: NetCallNode) {
            let collect = new ByteArray();
            collect.endian = egret.Endian.BIG_ENDIAN;
            collect.writeUTFBytes(node.command);
            collect.length = 32;
            collect.position = 32;
            collect.writeInt(node.id);
            collect.writeShort(1);
            collect.writeShort(3);
            collect.writeShort(0);
            collect.writeShort(1);
            collect.writeInt(0);
            let request = node.request;
            if (request) {
                let msgBuffer = request.constructor.encode(request).finish();
                let msgByte = new ByteArray(msgBuffer);
                collect.writeBytes(msgByte, 0, msgByte.length);
            }
            let out = new ByteArray();
            out.endian = egret.Endian.BIG_ENDIAN;
            out.writeInt(collect.length + 4);
            out.writeBytes(collect);
            let logContent = 'send ' + node.command;
            if (request) {
                logContent += '[' + egret.getQualifiedClassName(request) + ']: ' + this._commandMapper.stringifyProto(request);
            }
            this.socketLog(logContent);
            this._socket.writeBytes(out, 0, out.length);
            this._socket.flush();
        }

        private onReceiveData(evt: any) {
            let byte = new egret.ByteArray();
            this._socket.readBytes(byte);
            byte.position = 0;
            // if(byte.length == 0)return;
            let packageLen = byte.readInt();
            let receiveCommand = byte.readUTFBytes(32);
            byte.position = 36;
            let packageId = byte.readInt();
            let responseByte = new egret.ByteArray();
            responseByte.writeBytes(byte, 52, packageLen - 52);
            let responseReader = new Uint8Array(responseByte.buffer, 0, responseByte.buffer.byteLength);
            if (this._commandMapper.isPush(receiveCommand)) {
                //推送
                let responseClass = this._commandMapper.getResponseClass(receiveCommand);
                let logContent = 'gotPush ' + receiveCommand;
                if (responseClass) {
                    let responseObj = responseClass.decode(responseReader);
                    // if(DEBUG){
                    logContent += '[' + egret.getQualifiedClassName(responseClass) + ']: ' + this._commandMapper.stringifyProto(responseObj)
                    // }
                    this.socketLog(logContent);
                    let callList = this._pushMap.get(receiveCommand);
                    if (callList) {
                        for (let i = 0, len = callList.length; i < len; i++) {
                            callList[i].invoke(responseObj);
                        }
                    }
                } else {
                    warn(logContent + ' but not find the decode class')
                }
            } else {
                let isWildPush = receiveCommand.indexOf("push@") >= 0;
                if (isWildPush) {
                    warn('wild push:' + receiveCommand);
                    return;
                }
                let commonResult: CommonResult = CommonResult.decode(responseReader);
                let packageIndex = this._packageIdList.indexOf(packageId);
                if (commonResult.actionResult == 1) {
                    //正常返回
                    if (packageIndex < 0) {
                        this.socketLog('have not send the package');
                    }
                    else {
                        let packageNode = this._packageList[packageIndex];
                        let responseClass = this._commandMapper.getResponseClass(packageNode.command);
                        let logContent = 'receive ' + packageNode.command;
                        if (responseClass) {
                            let responseObj = responseClass.decode(responseReader);
                            // if(DEBUG){
                            logContent += '[' + egret.getQualifiedClassName(responseClass) + ']: ' + this._commandMapper.stringifyProto(responseObj)
                            // }
                            this.socketLog(logContent);
                            packageNode.invokeResponse(responseObj);
                        }
                        else {
                            this.socketLog(logContent);
                            packageNode.invokeResponse(commonResult);
                        }
                        this._packageIdList.splice(packageIndex, 1);
                        this._packageList.splice(packageIndex, 1);
                        ObjectPool.recycleObj(packageNode);
                    }
                }
                else if (commonResult.actionResult == 2) {
                    //服务器出问题了
                    this.socketLog('service error: ' + commonResult.msg)
                }
                else if (commonResult.actionResult == 0) {
                    //接口调用错误
                    this.socketLog('error call: ' + commonResult.msg);
                    // Dispatcher.Instance.dispatchEventWith(UIEvent.SHOW_MESSAGE, false, {
                    //     msg: commonResult.msg,
                    //     color: Color.MSG_GREEN
                    // });
                    if (packageIndex >= 0) {
                        let packageNode = this._packageList[packageIndex];
                        packageNode.errorResponse(commonResult);
                    }
                }
                else {
                    this.socketLog('service error: ' + commonResult.msg)
                }
            }
        }

        private onSocketConnect(evt: any) {
            this.socketLog('connect to [' + this._server.host + ':' + this._server.port + ']');
            if (this._onConnectCall) {
                let tempCall = this._onConnectCall;
                this._onConnectCall = null;
                tempCall.apply(this._onConnectObj);
            }
            this.startHeartBeat();
        }

        private _repeatCount = -1;
        private _timeOutVO;

        private onSocketClose(evt: any) {
            this.socketLog("close");
            // if (PlayerModule.instance.roleInfo)
            //     this.reconnect();
        }

        private reconnect() {
            if (this._repeatCount == -1) {
                this._repeatCount = 0;
                // if (PlayerModule.instance.roleInfo)
                //     Dispatcher.Instance.dispatchUIEventWith(UIEvent.OPEN, UIType.Reconnect);
                this.doReconnectLink();
            }
        }

        private doReconnectLink() {
            this.clearReconnectTimeout();
            if (this._repeatCount < 2) {
                this._timeOutVO = TimerProxy.instance.setTimeout(this.doReconnectLink, 1000, this);
            }
            else {
                // Dispatcher.Instance.dispatchEventWith(UIEvent.SHOW_MESSAGE,false,{msg:"重连失败，即将刷新游戏"});
                setTimeout(() => {
                    // if (GameConfig.chooseServerUrl)
                    //     if (GameConfig.PLAT_ID == PlatformSig.RELEASE_KING_NET) {
                    //         console.log('---- reload ----');
                    //         RechargeModel.Instance.reload();
                    //     }
                    //     else {
                    //         console.log('---- reload ----');
                    //         window.location.replace(GameConfig.chooseServerUrl);
                    //     }
                    //
                    // else {
                    console.log('---- reload ----');
                    window.location.reload(true);
                    // }

                }, 1500);
                return;
            }
            this._repeatCount++;
            // Dispatcher.Instance.dispatchEventWith(UIEvent.SHOW_MESSAGE,false,{msg:"断线重连: 第" + this._repeatCount + "次"});
            if (!this._socket.connected) {
                this.connectToServer();
                this._onConnectCall = this.excuteReconnectLink;
                this._onConnectObj = this;
            }
            else
                this.excuteReconnectLink();
        }

        private excuteReconnectLink() {
            let param: PlayerReconnectParam = new PlayerReconnectParam();
            // let showMessage = PlayerModule.instance.roleInfo != null;
            // param.sessionKey = showMessage ? PlayerModule.instance.roleInfo.sessionKey : GameConfig.tempSession;
            // this.sendRequest(CommandMap.reconnect, param, this, (r: PlayerReconnectResult) => {
            //     if (showMessage) {
            //         Dispatcher.Instance.dispatchEventWith(UIEvent.SHOW_MESSAGE, false, {msg: "重连成功"});
            //         Dispatcher.Instance.dispatchEventWith(UIEvent.CLOSE_ALL);
            //     }
            //
            //     PlayerModule.instance.me.scRole.sessionKey = r.sessionKey;
            //     this.clearReconnectTimeout();
            //     this._repeatCount = -1;
            // });
        }

        private clearReconnectTimeout() {
            if (this._timeOutVO)
                TimerProxy.instance.clearTimeVO(this._timeOutVO);
            this._timeOutVO = null;
        }

        private onIoError(evt: any) {
            this.socketLog("IoError");
            // this.reconnect();
        }

        private socketLog(msg: string) {
            // if (!GameConfig.isReleaseVersion)
            //     log(this.LOG_MARK + '[' + time.formatStr(GameConfig.localTime) + ']' + msg);
            // else {
            //     Analytics.Instance.recordNetMessage(msg);
            // }
        }

        //heartBeat
        private _heartBeatTimer: TimerVO;

        private startHeartBeat() {
            if (!this._heartBeatTimer)
                this._heartBeatTimer = TimerProxy.instance.setInterval(this.heartBeat, 40000, 0, this);
        }

        private heartBeat() {
            this.sendRequest('system@heartbeat')
        }
    }
}
